/**
 * Samsung Project
 * Copyright (c) 2007-2008 Mobile XG, Samsung Electronics, Inc.
 * All right reserved.
 *
 * This software is the confidential and proprietary information
 * of Samsung Electronics Inc. ("Confidential Information"). You
 * shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement
 * you entered into with Samsung Electronics.
 */

/**
 * @file	fgl.c
 * @brief	This is the global-register-setting file.
 * @author	Thomas, Kim
 * @version	1.5
 */
#include <stdlib.h>
#include <string.h>
#include "fgl.h"


/***************************************************************************** 
 * 
 *  Control Block
 * 
 *****************************************************************************/

FGLAPI fgl_pipeline_status ( uint32 * flag )
{
    READREGP(FGGB_PIPESTATE, *flag);    
}

FGLAPI fgl_pipeline_isempty ( const uint32 flag )
{
	volatile uint32 rval = 0;

	do
	{
		READREGP(FGGB_PIPESTATE, rval);
	} while(rval & flag);    
}

FGLAPI fgl_flush_cache ( const uint32 flag )
{
	volatile uint32 rval = 0;

	WRITEREG(FGGB_CACHECTL, flag);

	do
	{
		READREGP(FGGB_CACHECTL, rval);
	} while(rval & flag);
}

FGLAPI fgl_soft_reset ( void )
{
	volatile uint32 i;
	
	WRITEREG(FGGB_RESET, 1);
	
	/* delay */
	for(i = 0; i < 50; i++)
    {
#if TARGET_PLATFORM == LINUX
        __asm__ __volatile__ ("nop");
#elif TARGET_PLATFORM == WINCE
        __asm ("nop");
#elif TARGET_PLATFORM == FIRMWARE
        __asm {nop}
#endif
    }

	WRITEREG(FGGB_RESET, 0);    
}

FGLAPI fgl_version ( uint32 * major, uint32 * minor )
{
    uint32 ver;
    READREGP(FGGB_VERSION, ver);
    
    *major = ((ver & 0xFF000000) >> 24);
    *minor = (ver & 0xFFFFFF);    
}

FGLAPI fgl_clear_pending_int ( void )
{
    WRITEREG(FGGB_INTPENDING, 0x0);    
}

FGLAPI fgl_get_pending_int ( uint32 * state )
{
    READREGP(FGGB_INTPENDING, *state);    
}


FGLAPI fgl_enable_int ( void )
{
    WRITEREG(FGGB_INTMASK, 1);   
}

FGLAPI fgl_disable_int ( void )
{
    WRITEREG(FGGB_INTMASK, 0);   
}

/*
 * Specifies the related block to generate interrupt.
 */ 
FGLAPI fgl_interrupt_mask ( const uint32 flag )
{
    WRITEREG(FGGB_PIPEMASK, flag);    
}

FGLAPI fgl_interrupt_target ( const uint32 flag )
{
    WRITEREG(FGGB_PIPETGTSTATE, (flag & PSF_ALL));    
}

/*
 *  Has been preserved first pipeline state when interrupts occured.
 */ 
FGLAPI fgl_interrupt_status  ( uint32 * status )
{
    READREGP(FGGB_PIPEINTSTATE, *status);        
}


/***************************************************************************** 
 * 
 *  Host Interface Block
 * 
 *****************************************************************************/

FGLAPI fgl_get_empty_slots ( uint32 * slots )
{
    READREGP(FGHI_DWSPACE, *slots);
}

FGLAPI fgl_transfer_fifo ( void * src, uint32 size )
{
#if 1
	uint32 space = 0, nth = 0;
	unsigned char *ptr = src, bits[4]; 
	int32 i, diff;

	do {

		if(space == 0)
		{
			do {
				READREGP(FGHI_DWSPACE, space);
			} while(!space);
		}

		diff = size - nth;
		
		if(diff < 4)
		{
			bits[0] = bits[1] = bits[2] = bits[3] = 0x00;

			for(i=0; i<diff; i++)
				bits[i] = ptr[nth++];

			WRITEREG(FGHI_DWENTRY, *((uint32 *)bits));
			space --;
		}
		else
		{
			WRITEREG(FGHI_DWENTRY, *((uint32 *)&(ptr[nth])));
			space --;
			nth += 4;
		}

	} while(nth < size);    
#else
	//unsigned int *pdst_port = FGHI_FIFO_ENTRY; 

	unsigned int offset = (unsigned int)psrc_data & 0x0000001F;
	unsigned int alloc_size = 0;

	if(psrc_data == FGL_NULL || size == 0)
	{
		// For debug
		//WRITEREG(0x00000104, 0xFFFFFFFF);
		return;
	}

	do
	{
		do
		{
			alloc_size = fglSysReservePortAlloc(size, 1);
		} while (alloc_size == 0);
		
		/* Write out dwords until we reach 32-byte alignment */
		if (offset > 0)
		{
			unsigned int before_align = 8 - (offset >> 2);

			if (before_align > alloc_size)
			{
				before_align = alloc_size;
				alloc_size = 0;
			}
			else
			{
				alloc_size -= before_align;
			}

			while (before_align-- > 0)
			{
				WRITEREG(FGHI_FIFO_ENTRY, *(psrc_data++));
				size --;
			}
			offset = (unsigned int)psrc_data & 0x0000001F;			
		}

		/* This works on aligned data */
		while (alloc_size & 0xFFFFFFF8)
		{

#if TARGET_PLATFORM == WIN32_VIP 
			WRITEREG(FGHI_FIFO_ENTRY, *(psrc_data++));
			WRITEREG(FGHI_FIFO_ENTRY, *(psrc_data++));
			WRITEREG(FGHI_FIFO_ENTRY, *(psrc_data++));
			WRITEREG(FGHI_FIFO_ENTRY, *(psrc_data++));
			WRITEREG(FGHI_FIFO_ENTRY, *(psrc_data++));
			WRITEREG(FGHI_FIFO_ENTRY, *(psrc_data++));
			WRITEREG(FGHI_FIFO_ENTRY, *(psrc_data++));
			WRITEREG(FGHI_FIFO_ENTRY, *(psrc_data++));

#elif TARGET_PLATFORM == FPGA_BOARD
            unsigned int a0, a1, a2, a3, a4, a5, a6, a7;
			__asm
			{
    			LDMIA psrc_data!,{a0, a1, a2, a3, a4, a5, a6, a7}
    			STMIA FGHI_FIFO_ENTRY, {a0, a1, a2, a3, a4, a5, a6, a7}
			}
#endif
			alloc_size -= 8;
			size -= 8;
		}
	} while (size & 0xFFFFFFF8);

	/* Write out the last < 8 dwords */
	while (size-- > 0)
	{
		WRITEREG(FGHI_FIFO_ENTRY, *psrc_data);
		++psrc_data;
	}

#endif
}


/*
 * In case used the point sprite mode, the number of vertex shader output must be plus one.
 */
FGLAPI fgl_host_interface ( void * hostinfo )
{
    struct host_interface_info * p;
    ASSERT( hostinfo != 0 );
    p = (struct host_interface_info *)hostinfo;

    ASSERT(p->vertex_shader_out < 16); // cheolkyoo, kim must be checked
    WRITEREG(FGHI_CONTROL, ((p->vertex_shader_out) | (((p->vtxcache)?1:0) << 4) | 
                           (((p->autoinc)?1:0) << 16)  | (p->idx_type) << 24 | (((p->vtxbuf)?1:0)<< 31)));
}

FGLAPI fgl_index_offset ( uint32 offset )
{
    WRITEREG(FGHI_IDXOFFSET, offset);
}

FGLAPI fgl_attribute ( const uint32 count, void * attrib )
{
    uint32 i;
    struct attribute_info * p;
    
    ASSERT( attrib != 0 );
    p = (struct attribute_info *)attrib;
    
    ASSERT( count < 11 );
    ASSERT( p->component > 0 && p->component < 5 );
    
#if 1

    for (i = 0; i < (count - 1); i++)
    {
        WRITEREG((FGHI_ATTRIB + i * 4), (p[i].x | (p[i].y << 2) | (p[i].z << 4) | (p[i].w << 6) | 
                                        ((p[i].component - 1) << 8) | (p[i].dtype << 12)));
    }
 
     
    WRITEREG((FGHI_ATTRIB + i * 4), ((0x80000000) | (p[i].x) | (p[i].y << 2) | (p[i].z << 4) | (p[i].w << 6) | 
                                    ((p[i].component - 1) << 8) | (p[i].dtype << 12)));
       
#endif

}

FGLAPI fgl_transfer_vtxbuf ( void * vbdata, uint32 words )
{
    // TODO : write code using ARM assembler
#if 1
    uint32 i;
    
    for (i = (words >> 11); i != 0; i--)
    {
        memcpy((void *)FGHI_VBDATA, vbdata, (FGHI_VBDATA_BURST_WSIZE * 4)); 
        vbdata = (uint32 *)vbdata + FGHI_VBDATA_BURST_WSIZE;    
    }
    
    words = (words & (FGHI_VBDATA_BURST_WSIZE - 1));
 
    if (words)
        memcpy((void *)FGHI_VBDATA, vbdata, (words * 4));

#endif        
    
#if 0    
    uint32 i;
    
    for (i = (words >> 2); i != 0; i--)
    {
        WRITEREG(FGHI_VBDATA, *(vbdata++));
        WRITEREG(FGHI_VBDATA, *(vbdata++));
        WRITEREG(FGHI_VBDATA, *(vbdata++));
        WRITEREG(FGHI_VBDATA, *(vbdata++));
    }
    
    for (i = (words & 3); i != 0; i--)
        WRITEREG(FGHI_VBDATA, *(vbdata++));
#endif
   
#if 0    
    do 
    {
        WRITEREG(FGHI_VBDATA, *(vbdata++));
    } while (--words != 0)
#endif    
}

FGLAPI fgl_vtxbuf_attribute ( uint32 index, uint32 stride, uint32 count, uint32 base )
{
    ASSERT(index < 10);
    WRITEREG((FGHI_ATTRIB_VBCTRL + (index << 2)), (count | (stride << 24)));    
    WRITEREG((FGHI_ATTRIB_VBBASE + (index << 2)), base);
}


/***************************************************************************** 
 * 
 *  Primitive Engine 
 * 
 *****************************************************************************/

FGLAPI fgl_vertex ( void * context )
{
    uint32 reg_val = 0;
    struct vertex_context * pctx = (struct vertex_context *)context;
    
    ASSERT(pctx->vertex_shader_out < 10);
    
    reg_val = (( pctx->type << 19) | (pctx->vertex_shader_out << 10));
    
    if( pctx->flat_color_pos )
    {
        reg_val |= ( (1 << 9) | (pctx->flat_color_pos & 0x1FF));
    }

	WRITEREG(FGPE_VERTEX_CONTEXT, reg_val);    
}

FGLAPI fgl_viewport ( void * viewport )
{
    struct viewport_info vpdata;
    float half_x   = 0.0;
    float halp_y   = 0.0;
    float center_x = 0.0;
    float center_y = 0.0;
     
    memcpy((void *)&vpdata, viewport, sizeof(struct viewport_info));

	half_x = (float)vpdata.width  * 0.5;
	halp_y = (float)vpdata.height * 0.5;

	center_x = (float)vpdata.x + half_x;
	center_y = 0.0;

	if (vpdata.flip) {
	    center_y = (float)(vpdata.win_height - vpdata.y) - halp_y;
	    halp_y *= (-1.f);
	} else {
	    center_y = (float)(vpdata.y + halp_y);
	}

	WRITEREGF(FGPE_VIEWPORT_OX, center_x);
	WRITEREGF(FGPE_VIEWPORT_OY, center_y);
	WRITEREGF(FGPE_VIEWPORT_HALF_PX, half_x);
	WRITEREGF(FGPE_VIEWPORT_HALF_PY, halp_y);
}

FGLAPI fgl_depth_range ( const float near, const float far)
{
	ASSERT((near >= 0.0) && (near <= 1.0));
	ASSERT((far  >= 0.0) && (far  <= 1.0));
	WRITEREGF(FGPE_DEPTHRANGE_HALF_F_SUB_N, ((far - near) * 0.5));
	WRITEREGF(FGPE_DEPTHRANGE_HALF_F_ADD_N, ((far + near) * 0.5));
}



/***************************************************************************** 
 * 
 *  Rasterizer 
 * 
 *****************************************************************************/

/*
 * controls whether pixel is sampled at the center or corner.
 */
FGLAPI fgl_pixel_sampos ( const fglenum pos )
{
    WRITEREG(FGRA_PIXSAMP, pos);
}

FGLAPI fgl_disable_doffset ( void )
{
    WRITEREG(FGRA_DOFFEN, 0);
}

FGLAPI fgl_doffset    ( const float factor, const float units)
{
    WRITEREGF(FGRA_DOFFFACTOR, factor);
    WRITEREGF(FGRA_DOFFUNITS,  units);    
}

FGLAPI fgl_doffset_r  ( const float rval )
{
    WRITEREGF(FGRA_DOFFRIN, rval);   
}

FGLAPI fgl_disable_cullface ( void )
{
    WRITEREG(FGRA_BFCULL, 0);
}

FGLAPI fgl_cull_face ( const bool clockwise, const fglenum mode )
{
    if (clockwise)
        WRITEREG(FGRA_BFCULL, (0xC | (mode & 0x3)));
    else
        WRITEREG(FGRA_BFCULL, (0x8 | (mode & 0x3)));
}

/*
 * Sets the x, y clipping plan.
 */
FGLAPI fgl_clip_plan ( const uint32 left, const uint32 right, const uint32 bottom, const uint32 top )
{
    ASSERT((left < 4096) && (right < 4096));
    ASSERT((bottom < 4096) && (top < 4096));
    WRITEREG(FGRA_XCLIP, ((right << 16) | left));
    WRITEREG(FGRA_YCLIP, ((top << 16) | bottom));
}

/*
 * Generate the LOD coefficent value that Fragment Shader calculate the LOD value for texturing.
 */
FGLAPI fgl_lodctrl ( const uint32 lod ) 
{
    ASSERT(!(lod & 0xFF000000));
	WRITEREG(FGRA_LODCTL, lod);
}

FGLAPI fgl_point_width ( const float width )
{
    WRITEREGF(FGRA_PWIDTH, width);
}

FGLAPI fgl_psize_minmax ( const float min, const float max )
{
    ASSERT((min >= 0.0) && (max <= 2048.0));
    WRITEREGF(FGRA_PSIZE_MIN, min);
    WRITEREGF(FGRA_PSIZE_MAX, max);
}

/*
 * Used only in point sprite rendering.
 * Only one bit chooses generated texture coordinate for point sprite.
 * Alway replace coord must be set last position of attributes .
 */
FGLAPI fgl_replace_coord( const uint32 coord )
{
    ASSERT(coord < 8);
    WRITEREG(FGRA_COORDREPLACE, (1 << coord));
}

FGLAPI fgl_line_width ( const float width )
{
    WRITEREGF(FGRA_LWIDTH, width);   
}


/***************************************************************************** 
 * 
 *  Texture Units Register-level API
 * 
 *****************************************************************************/

static void _gen_mipmap_offset ( const fglenum format, uint32 width, uint32 height, uint32 * offset )
{
	uint32 i       = 0;
	uint32 bigger  = 0;
	uint32 tmp     = 0;
	uint32 texsize = width * height;
	
    switch(format)
    {
	case TFMT_ARGB1555 :
	case TFMT_RGB565   : 
	case TFMT_ARGB4444 : 
	case TFMT_DEPTH24  : 
	case TFMT_IA88     : 
	case TFMT_I8       : 
	case TFMT_ARGB8888 : 
	case TFMT_1BPP     : 
	case TFMT_2BPP     : 
	case TFMT_4BPP     : 
	case TFMT_8BPP     : 
        {
        	offset[0] = texsize;
        	bigger = width > height ? width : height;
        	bigger >>= 1;
        	for( i = 1; (bigger > 0 && i < 10); i++) {
            	width  = (width  <= 1) ? 1 : (width  >> 1);
        		height = (height <= 1) ? 1 : (height >> 1);
        		texsize += width * height;
        		offset[i] = texsize;
        		bigger >>= 1;
           	}
        }
        break;
	case TFMT_S3TC :
    	{
        	if((texsize == 4) || (texsize == 1)) {
        		offset[0] = 16;
        	    break;
        	} else {
        		offset[0] = texsize;
        	}

        	bigger = width > height ? width : height;
        	bigger >>= 1;
        	
        	for( i = 1; (bigger > 0 && i < 10); i++) {
            	width  = (width  <= 1) ? 1 : (width  >> 1);
        		height = (height <= 1) ? 1 : (height >> 1);
        		tmp = width * height;

        		if((tmp == 4) || (tmp == 1)) {
        			texsize += 16;
        		} else {
        			texsize += tmp;
       		    }
        		
        		bigger >>= 1;
        		offset[i] = texsize;
           	}
    	}
	    break;
	case TFMT_Y1VY0U : 
	case TFMT_VY1UY0 : 
	case TFMT_Y1UY0V : 
	case TFMT_UY1VY0 :
    	{
        	if(texsize == 1) {
        		offset[0] = 2;
        	    break;
        	} else {
        		offset[0] = texsize;
        	}

        	bigger = width > height ? width : height;
        	bigger >>= 1;
        	
        	for( i = 0; (bigger > 0 && i < 10); i++) {
            	width  = (width  <= 1) ? 1 : (width  >> 1);
        		height = (height <= 1) ? 1 : (height >> 1);
        		tmp = width * height;

        		if((tmp == 1)) {
        			texsize += 2;
        		} else {
        			texsize += tmp;
       		    }
        		
        		bigger >>= 1;
        		offset[i] = texsize;
           	}
    	}
	    break;
   	default :
        ASSERT(0);
        break;
    }
    
    *(offset + 12)  = 0;
    *(offset + 13)  = i;  
}

FGLAPI fgl_texture ( const uint32 unit, void * texinfo )
{
    struct texture_info tex;
    uint32 buf[18];
    
    memcpy((void *)&tex, texinfo, sizeof(struct texture_info));

	ASSERT(unit <= 7);
 	ASSERT((tex.usize < 2048) && (tex.vsize < 2048) && (tex.psize < 2048));
 
    buf[0] = ((tex.mipmap_filter)              |  
             ((tex.use_minfilter  ? 1:0) << 2) | 
             ((tex.use_magfilter  ? 1:0) << 3) | 
             ((tex.non_parametric ? 1:0) << 4) | 
             (tex.wmode   << 6)                | 
             (tex.vmode   << 8)                | 
             (tex.umode   << 10)               | 
             (tex.texmfmt << 12)               | 
             (tex.palfmt  << 17)               | 
             ((tex.rgba_order    ? 1:0) << 19) | 
             ((tex.use_expansion ? 1:0) << 20) | 
             (tex.ckey    << 21)               | 
             (tex.textype << 27));
    
    buf[1] = tex.usize;
    buf[2] = tex.vsize;
    buf[3] = tex.psize;
  
    if(tex.mipmap_filter) {
        _gen_mipmap_offset(tex.texmfmt, tex.usize, tex.vsize, &buf[4]);
        memcpy((void *)(FGTU_TSTA0 + unit * 0x50), (void *)buf, sizeof(buf));    
    }
    else {
        WRITEREG((FGTU_TSTA0  + unit * 0x50), buf[0]);   
        WRITEREG((FGTU_USIZE0 + unit * 0x50), buf[1]);
        WRITEREG((FGTU_VSIZE0 + unit * 0x50), buf[2]);
        WRITEREG((FGTU_WSIZE0 + unit * 0x50), buf[3]);
        WRITEREG((FGTU_TBADD0 + unit * 0x50), tex.address);
    }
}

FGLAPI fgl_mipmap_level ( const uint32 unit, const uint32 min, const uint32 max)
{
	ASSERT( unit > 7 );
	ASSERT( (max < 12) && (min < 12) && (min > max) );
    WRITEREG((FGTU_T_MIN_L0 + unit * 0x50), min);
    WRITEREG((FGTU_T_MAX_L0 + unit * 0x50), max);        
}

FGLAPI fgl_ckey ( const uint32 unit, const uint32 r, const uint32 g, const uint32 b )
{
	ASSERT(unit <= 1);
	WRITEREG((FGTU_CKEY1 + unit * 4), (b | (g << 8) | (r << 16)));    
}

FGLAPI fgl_ckey_yuv ( const uint32 u /*cb*/, const uint32 v/*cr*/ )
{
    WRITEREG(FGTU_CKYUV, (v | (u << 8)));    
}

FGLAPI fgl_ckey_mask ( const uint32 mask )
{
    WRITEREG(FGTU_CKMASK, (mask & 0x7));    
}

FGLAPI fgl_load_palettet ( const void * palette, const fglenum format, const uint32 size )
{
	uint32 i = 0;
	ushort * uspal = 0;
	uint32 * uipal = 0;
	
	ASSERT(size <= 256);
 
    switch(format)
    {
    case PAL_ARGB1555 :
    case PAL_RGB565   :
    case PAL_ARGB4444 :
        {
           uspal = (ushort *)palette;
           for( i = size; i > 0; i-- ) {
                WRITEREG(FGTU_PALETTE_ADDR, i);
                WRITEREGS(FGTU_PALETTE_IN, uspal[i]);
            }
        }
    	break;
    case PAL_ARGB8888 :
        {
            uipal = (uint32 *)palette;
            for( i = size; i > 0; i-- ) {
                WRITEREG(FGTU_PALETTE_ADDR, i);
                WRITEREG(FGTU_PALETTE_IN, uipal[i]);
            }
        }
    	break;
   	default :
        ASSERT(0);
        break;
    }    

}

static bool _power_of_two ( uint32 value ) 
{
	if (!value)
		return true;

	while (!(value & 1))
		value >>= 1;

	value >>= 1;

	return value == 0;
}

static uint32 _log ( uint32 value ) 
{
	uint32 result = 0;

	while (value > 1) {
		++result;
		value >>= 1;
	}

	return result;
}


FGLAPI fgl_vtxtex ( const uint32 unit, void * vtxtex )
{
	struct vtxtex_info * p = (struct vtxtex_info *)vtxtex;
	
	ASSERT( unit < 4 );
	ASSERT( _power_of_two(p->usize) );
	ASSERT( _power_of_two(p->vsize) );
	ASSERT( _log(p->usize) <= 11 );
	ASSERT( _log(p->vsize) <= 11 );

	WRITEREG((FGTU_VTSTA + unit * 8), ((_log(p->vsize)) | ((_log(p->usize)) << 4) | (p->vmode << 8) | (p->umode << 10)));
    WRITEREG((FGTU_VTBADDR + unit * 8), p->base);    
    
}


/***************************************************************************** 
 * 
 *  Per-fragment Unit Register-level API
 * 
 *****************************************************************************/

FGLAPI fgl_scissor ( const int32 x, const int32 y, const int32 width, const int32 height )
{
	ASSERT(x < 4096 && y < 4095 && width < 4096 && height < 4096);

	WRITEREG(FGPF_SCISSOR_X, (uint32)((1 << 31) | ((x + width) << 16) | x ));
	WRITEREG(FGPF_SCISSOR_Y, (uint32)((y << 16) | (y - height)));
}

FGLAPI fgl_disable_scissor ( void )
{
    WRITEREG(FGPF_SCISSOR_X, 0);   
}

FGLAPI fgl_alpha ( const fglenum func, const uint32 ref )
{
    ASSERT(ref < 256); 
    WRITEREG(FGPF_ALPHAT, ((ref << 4) | (func << 1) | 1));
}

FGLAPI fgl_disable_alpha ( void )
{
    WRITEREG(FGPF_ALPHAT, 0);    
}

FGLAPI fgl_stencil ( void * stencilinfo )
{
	struct stencil_info sinfo;
    memcpy((void *)&sinfo, stencilinfo, sizeof(struct stencil_info));
	ASSERT( sinfo.ref <= 256 ); 
    
// Thomas, Kim : software workaround 
#ifdef WORKAROUND_STENCIL_FUNC_SWAP
    switch(sinfo.func)
    {
    case CF_LESS:
        sinfo.func = CF_GREATER;
        break;
    case CF_LEQUAL:
        sinfo.func = CF_GEQUAL;
        break;
    case CF_GREATER:
        sinfo.func = CF_LESS;
        break;
    case CF_GEQUAL:
        sinfo.func = CF_LEQUAL;
        break;
    }
#endif    

    switch (sinfo.face)
    {
    case BACKFACE:
        WRITEREG(FGPF_FRONTST, ((sinfo.zpass << 29) | (sinfo.zfail << 26) | (sinfo.sfail << 23) | 
                                (sinfo.mask << 12) | (sinfo.ref << 4) | (sinfo.func << 1) | 1));
        break;
    case FRONTFACE:
        WRITEREG(FGPF_BACKST, ((sinfo.zpass << 29) | (sinfo.zfail << 26) | (sinfo.sfail << 23) | 
                               (sinfo.mask << 12) | (sinfo.ref << 4) | (sinfo.func << 1)));
        break;
    case FRONTBACK:
        WRITEREG(FGPF_FRONTST, ((sinfo.zpass << 29) | (sinfo.zfail << 26) | (sinfo.sfail << 23) | 
                                (sinfo.mask << 12) | (sinfo.ref << 4) | (sinfo.func << 1) | 1));
        WRITEREG(FGPF_BACKST, ((sinfo.zpass << 29) | (sinfo.zfail << 26) | (sinfo.sfail << 23) | 
                               (sinfo.mask << 12) | (sinfo.ref << 4) | (sinfo.func << 1)));
        break;
   	default :
        ASSERT(0);
        break;
    }
        
}

FGLAPI fgl_disable_stencil ( void )
{
    WRITEREG(FGPF_FRONTST, 0);
}

FGLAPI fgl_depth ( const fglenum func)
{
    WRITEREG(FGPF_DEPTHT, ((func << 1) | 1));
}

FGLAPI fgl_disable_depth ( void )
{
    WRITEREG(FGPF_DEPTHT, 0);    
}

FGLAPI fgl_blend ( void * blendinfo )
{
    struct blend_info * p = (struct blend_info *)blendinfo;
    WRITEREG(FGPF_BLEND, (1 | (p->color << 1) | (p->salpha << 5) | (p->dcolor << 9) | 
                         (p->dalpha << 13) | (p->cequa << 17) | (p->aequa << 20)));
                         
    WRITEREG(FGPF_CCLR, p->color);                         
}

FGLAPI fgl_disable_blend ( void )
{
    WRITEREG(FGPF_BLEND, 0);    
}

FGLAPI fgl_blend_color ( const uint32 r, const uint32 g, const uint32 b, const uint32 a )
{
    WRITEREG(FGPF_CCLR, (((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff)));
}

FGLAPI fgl_logicop ( const fglenum opcode )
{
    WRITEREG(FGPF_LOGOP, ((opcode << 5) | (opcode << 1) | 1));
}

FGLAPI fgl_disable_logicop ( void )
{
    WRITEREG(FGPF_LOGOP, 0);
}

FGLAPI fgl_color_mask ( const bool r, const bool g, const bool b, const bool a )
{
	WRITEREG(FGPF_CBMSK, (((r)?8:0) | ((g)?4:0) | ((b)?2:0) | ((a)?1:0)));    
}

FGLAPI fgl_stencil_depth_mask ( const uint32 fsmask, const uint32 bsmask, const bool dmask )
{
	ASSERT( fsmask <= 256 && bsmask <= 256 );
    WRITEREG(FGPF_DBMSK, ((fsmask << 24) | (bsmask << 16) | (dmask)?1:0));    
}

FGLAPI fgl_colorbuf ( void * fbinfo )
{
	struct fbctrl_info * p = (struct fbctrl_info *)fbinfo;
	ASSERT( p->threshold <= 256 && p->alpha <= 256 ); 
	WRITEREG(FGPF_FBCTL, ((p->opaque << 20) | (p->threshold << 12) | (p->alpha << 4) | 
	                      (p->dither << 3)  | p->format));        
}

FGLAPI fgl_depthbuf_base ( const uint32 base )
{
    WRITEREG(FGPF_DBADDR, base);    
}

FGLAPI fgl_colorbuf_base ( const uint32 base )
{
    WRITEREG(FGPF_CBADDR, base);    
}

FGLAPI fgl_framebuf_stride ( const uint32 stride )
{
    WRITEREG(FGPF_FBW, stride);    
}


/***************************************************************************** 
 * 
 *  Vertex/Fragment Shader Register-level API
 * 
 *****************************************************************************/

int32 fgl_execute_mode ( fglenum mode )
{
	uint32 regval = 0;
	READREGP(FGPS_EXEMODE, regval);

	//if(regval == mode) return 0;

	if(mode) {
		if( rFGPS_IBSTATUS & 0x1 )     return 1;
		WRITEREG(FGPS_EXEMODE, 1);
	} else {
		if( rFGGB_PIPESTATE & PSF_FS ) return 1;
		WRITEREG(FGPS_EXEMODE, 0);
	}
	
	return 0;
}

FGLAPI fgl_vs_pc ( const uint32 start, const uint32 end, const bool endignore )
{
	ASSERT((start < 512) && (end < 512) && (end > start));
   	WRITEREG(FGVS_PCRANGE, (start | (end << 16) | ((endignore)?1:0 << 31)));
   	// TODO need a verification : copy pc into vs inside
   	WRITEREG(FGVS_CONFIG, 1);    
}

FGLAPI fgl_vs_inattrib ( const uint32 size )
{
	// Max attribute sizes are 10 in vertex shader
	ASSERT(size <= 10);
   	WRITEREG(FGVS_ATTRIBNUM, size);    
}

#if 0
uint32 fgl_write_cfmem   ( uint32 offset, uint32 size, float *data )
{
    uint32 i;
    uint32 * dst = (uint32 *)(FGVS_CFLOAT + (offset * 4));
    
    for ( i = 0; i < size; i++ )
    {
        WRITEREGF(dst++, *data++);	    
        offset++;
	}
	
	return offset;    
}
#endif

#define VSMAGIC     0x20205356
#define FSMAGIC     0x20205350
#define SHADERVER   0xFFFF0003


int32 fgl_load_vs ( const uint32 *program )
{
	uint32 * pmem  = 0;
	uint32 * pbody = 0;
	uint32   size  = 0;
	uint32   tmp   = 0;

	struct shader_hdr * phdr = (struct shader_hdr *)program;
	pbody = (uint32 *)&phdr[1];

	if ((phdr->magic != VSMAGIC) || (phdr->version != SHADERVER))
	{
		return 1;
	}

    if(phdr->in_tbl) {
        fgl_vs_inattrib(phdr->in_tbl);
        tmp += phdr->in_tbl;
    }
    
    if(phdr->out_tbl) tmp += phdr->out_tbl;
    if(phdr->sampler) tmp += phdr->sampler;
    
	size = phdr->instruct;
	
	if(size < 512) { 
		fgl_vs_pc(0, ((size >> 2) - 1), false);
		pmem = (uint32 *)FGVS_INSTMEM;
		pbody += tmp;

		do {
			WRITEREG(pmem++, *pbody++);
		} while(--size != 0);
	} 

	if(phdr->const_float) { 
		size = phdr->const_float;
        pmem = (uint32 *)FGVS_CFLOAT;
        
		do {
			WRITEREG(pmem++, *pbody++);
		} while(--size != 0);
	} 

	if(phdr->const_int) { 
		size = phdr->const_int;
        pmem = (uint32 *)FGVS_CINT;

		do {
			WRITEREG(pmem++, *pbody++);
		} while(--size != 0);
	} 

	if(phdr->const_bool) { 
		WRITEREG(FGVS_CBOOL, *pbody);
	}
	
	return 0;      
}

FGLAPI fgl_fs_pc ( const uint32 start, const uint32 end, const bool endignore )
{
	ASSERT((start < 512) && (end < 512) && (end > start));

	if (fgl_execute_mode(ACCSSIBLE)) {
	   ASSERT(0);
	   return; 
	} else {
    	WRITEREG(FGPS_PCSTART, start); 
    	WRITEREG(FGPS_PCEND, (end | ((endignore)?1:0 << 9)));
    	WRITEREG(FGPS_PCCOPY, 0x1);
	}
	
    while(rFGPS_IBSTATUS);
	
	if (fgl_execute_mode(EXECUTE)) 
	{
	    ASSERT(0);
	    return;
	}
}

FGLAPI fgl_fs_inattrib ( const uint32 size )
{
	ASSERT( size < 10 );

	if (fgl_execute_mode(ACCSSIBLE)) {
	   ASSERT(0);
	   return; 
	} else {
        WRITEREG(FGPS_ATTRIBNUM, size);
	}
	
	while(rFGPS_IBSTATUS & 0x1);

	if (fgl_execute_mode(EXECUTE)) 
	{
	    ASSERT(0);
	    return;
	}
}

int32 fgl_load_fs ( const uint32 * program )
{
	uint32 * pmem  = 0;
	uint32 * pbody = 0;
	uint32   size  = 0;
	uint32   tmp   = 0;

	struct shader_hdr * phdr = (struct shader_hdr *)program;
	pbody = (uint32 *)&phdr[1];

	if ((phdr->magic != FSMAGIC) || (phdr->version != SHADERVER))
	{
		return 1;
	}

    if(phdr->in_tbl) {
        fgl_fs_inattrib(phdr->in_tbl);
        tmp += phdr->in_tbl;
    }
    
    if(phdr->out_tbl) tmp += phdr->out_tbl;
    if(phdr->sampler) tmp += phdr->sampler;
    
	size = phdr->instruct;
	
	if(size < 512) { 
		fgl_fs_pc(0, ((size >> 2) - 1), false);
		pmem = (uint32 *)FGPS_INSTMEM;
		pbody += tmp;

		do {
			WRITEREG(pmem++, *pbody++);
		} while(--size != 0);
	} 

	if(phdr->const_float) { 
		size = phdr->const_float;
        pmem = (uint32 *)FGPS_CFLOAT;
        
		do {
			WRITEREG(pmem++, *pbody++);
		} while(--size != 0);
	} 

	if(phdr->const_int) { 
		size = phdr->const_int;
        pmem = (uint32 *)FGPS_CINT;

		do {
			WRITEREG(pmem++, *pbody++);
		} while(--size != 0);
	} 

	if(phdr->const_bool) { 
		WRITEREG(FGPS_CBOOL, *pbody);
	}

	return 0;     
}

int32 fgl_load_binary ( void * binary )
{
    uint32 size = 0;
    uint32 * pshader   = NULL;
    uint32 * pdata     = NULL;
    
    struct shader_bin_hdr hdr;
    
    memcpy((void *)&hdr, binary, sizeof(struct shader_bin_hdr));
    
    size += (hdr.instruct    * 4 * 4);
    size += (hdr.const_float * 4 * 4);
    size += (hdr.const_int   * 4 * 4);
    size += (hdr.const_bool  * 4);
    size += ((hdr.dcl_btype_tbl[0] + hdr.dcl_array_tbl[0]) * 4);
    
    pshader = (uint32 *)malloc(sizeof(struct shader_hdr) + size);
    pdata = pshader;
    
    if ( hdr.magic == VSMAGIC ) {
        *pdata++ = 0x20205356;
        *pdata++ = 0xFFFF0003;
        *pdata++ = 0x00000040;
        *pdata++ = (hdr.dcl_btype_tbl[0] + hdr.dcl_array_tbl[0]);
        *pdata++ = 0; 
        *pdata++ = 0;
        *pdata++ = (4 * hdr.instruct);
        *pdata++ = (4 * hdr.const_float);
        *pdata++ = (4 * hdr.const_int);
        *pdata++ = (4 * hdr.const_bool);
        
        // TODO: make the input attribute table
        pdata += (6 + (hdr.dcl_btype_tbl[0] + hdr.dcl_array_tbl[0]));
                
        memcpy((void *)pdata, (void *)((uint32 *)binary+(sizeof(struct shader_bin_hdr) >> 2)), size);
        
        fgl_load_vs(pshader);
        
    } else if ( hdr.magic == FSMAGIC ) {
        *pdata++ = 0x20205350;
        *pdata++ = 0xFFFF0003;
        *pdata++ = 0x00000040;
        *pdata++ = (hdr.dcl_btype_tbl[0] + hdr.dcl_array_tbl[0]);
        *pdata++ = 0;
        *pdata++ = 0;
        *pdata++ = (4 * hdr.instruct);
        *pdata++ = (4 * hdr.const_float);
        *pdata++ = (4 * hdr.const_int);
        *pdata++ = (4 * hdr.const_bool);
        
        // TODO: make the input attribute table
        pdata += (6 + (hdr.dcl_btype_tbl[0] + hdr.dcl_array_tbl[0]));
                
        memcpy((void *)pdata, (void *)((uint32 *)binary+(sizeof(struct shader_bin_hdr) >> 2)), size);
        
        fgl_load_fs(pshader);
        
    } else {
        ASSERT(0);
        return 1;
    }

    return 0;
}
 
#undef VSMAGIC 
#undef FSMAGIC
#undef SHADERVER
 
   				   

/* -----------------------< End of file >----------------------------- */



